package com.luo.bbs.ums.webapi.filter;

import com.alibaba.fastjson.JSON;
import com.luo.bbs.ums.webapi.security.UserLoginPrincipal;
import com.luo.common.restful.JsonResult;
import com.luo.common.restful.ResponseCode;

import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Component
@Slf4j
public class SSOFilter extends OncePerRequestFilter {
    @Value("${jwt.secretKey}")
    private String secretKey;
    @Value("${jwt.tokenHead}")
    private String tokenHead;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        log.debug("执行sso过滤器");
        SecurityContextHolder.clearContext();

        //从请求头获取jwt
        String authHeader = httpServletRequest.getHeader("Authorization");
        log.debug(authHeader);
        if (StringUtils.isBlank(authHeader) || authHeader.length() < 80 || !authHeader.startsWith(tokenHead)) {
            log.debug("jwt无效");
            filterChain.doFilter(httpServletRequest, httpServletResponse);
            return;
        }

        String authToken = authHeader.substring(tokenHead.length());
        log.debug(authToken);
        //如果Redis中已存在该jwt，说明已经登出过，直接return，需要重新登录
        //SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        String logoutKey = "user_logout_token_lock:";
        Boolean member = stringRedisTemplate.boundSetOps(logoutKey).isMember(authToken);
        if (member){
            log.info("从redis拿到登录的token:"+authToken);
            filterChain.doFilter(httpServletRequest, httpServletResponse);
            return;
        }

        //获取jwt的中间部分信息
        Claims claims = null;
        try {
            claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(authToken).getBody();
        } catch (ExpiredJwtException e) {
            log.warn("解析JWT失败：{}：{}", e.getClass().getName(), e.getMessage());
            JsonResult<Void> jsonResult = JsonResult.fail(
                    ResponseCode.ERR_JWT_EXPIRED, "登录信息已过期，请重新登录！");
            showJwtWrong(httpServletResponse, jsonResult);
            return;
        } catch (MalformedJwtException e) {
            log.warn("解析JWT失败：{}：{}", e.getClass().getName(), e.getMessage());
            JsonResult<Void> jsonResult = JsonResult.fail(
                    ResponseCode.ERR_JWT_PARSE, "无法获取到有效的登录信息，请重新登录！");
            showJwtWrong(httpServletResponse, jsonResult);
            return;
        } catch (SignatureException e) {
            log.warn("解析JWT失败：{}：{}", e.getClass().getName(), e.getMessage());
            JsonResult<Void> jsonResult = JsonResult.fail(
                    ResponseCode.ERR_JWT_PARSE, "无法获取到有效的登录信息，请重新登录！");
            showJwtWrong(httpServletResponse, jsonResult);
            return;
        } catch (Throwable e) {
            log.warn("解析JWT失败：{}：{}", e.getClass().getName(), e.getMessage());
            JsonResult<Void> jsonResult = JsonResult.fail(
                    ResponseCode.ERR_JWT_PARSE, "无法获取到有效的登录信息，请重新登录！");
            showJwtWrong(httpServletResponse, jsonResult);
            return;
        }

        String username = claims.get("username", String.class);
        Integer id = claims.get("id", Integer.class);
        String nickname = claims.get("nickname", String.class);
        UserLoginPrincipal principal = new UserLoginPrincipal();
        principal.setId(id);
        principal.setNickname(nickname);
        principal.setUsername(username);

        //生成一个对象放到上下文中,需要权限列表
        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        authorities.add(new SimpleGrantedAuthority("jia de"));
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(principal, null, authorities);

        SecurityContext context = SecurityContextHolder.getContext();
        context.setAuthentication(authenticationToken);
        log.debug("放入到上下文中的对象:{}", authenticationToken);

        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }

    private void showJwtWrong(HttpServletResponse httpServletResponse, JsonResult<Void> jsonResult) throws IOException {
        String jsonString = JSON.toJSONString(jsonResult);
        httpServletResponse.setContentType("text/html; charset=UTF-8");
        PrintWriter writer = httpServletResponse.getWriter();
        writer.println(jsonString);
        writer.close();
    }

}
